<?php
/**
 * Actions
 * 
 * Contains essential framework classes that should not be overridden or replaced.
 * @filesource
 */
/**
 * Namespace
 */
namespace framework\engine;
/**
 * Dependencies
 */
use framework\engine\Router;
use framework\Front;
/**
 * Collects and dispatches multiple controller actions in a recursive fashion.
 * 
 * @author Randy Casburn <randy@plurious.com>
 * @copyright Copyright Plurious Software, all rights reserved
 * @license MIT
 * @license http://www.opensource.org/licenses/mit-license.html
 * @link http://plurious.com/framework/documentation/classes/Front.html
 * @version 2.1
 * 
 * @uses Front
 * @uses Registry
 * @uses Document
 */
class Actions extends Router {

    protected $front;
    protected $context;
    protected $layout;

    /**
     * Grabs the registry, sets the output into the registry and sets up routing.
     * 
     */
    public function __construct() {
        $this->registry = Front::getInstance();
        $this->registry->set('output_data', array());
        parent::__construct();
    }

    /**
     * Creates the main action (or clears current main action if there is one),
     * then executes the new main action.
     * 
     * @uses Registry
     * @param mixed $route The controller name and optiontionally a directory name
     * @example error/not_found
     */
    public function run($route) {
        // Clear everything
        $this->clearAll();
        // Set main action in registry
        $this->addAction($route);
        // Execute the setup
        $this->execute($this->registry->getElement('page_actions', 'main_action'));
    }

    /**
     * Executes the action by instantiating requested controller and method.
     * 
     * A great deal of setup is required prior to exectuing an action. Existance of
     * the controller on the file system has already been validated.  If the method
     * does not exist, the method will force a 404.
     * 
     * @param mixed $action Array of action paths
     */
    private function execute($action) {
        //Instantiate controller
        $controller = new $action['class']();
        //Call the method requested
        if (is_callable(array($controller, $action['method']))) {
            $action = call_user_func_array(array($controller, $action['method']), $action['args']);
        } else {
            //If no method, force a 404 not found error
            $this->run('error/not_found');
        }
    }

    /**
     * Creates the action array with the file paths provided by the router.
     * 
     * Throws a 404 if a route cannot be resolved.
     * 
     * Sets up three sets of actions and one main action:
     * preActions - these run prior to the main action
     * childActions - Sub-page actions (controllers) that suplement the main action content
     * postActions - these run after the main action
     * mainAction - There is only one and is the main page controller from the URI
     * 
     * @uses Registry
     * @param mixed $route  Route in the form /context/controller/method/arg/arg/...
     * @param mixed $type  pre, main, post
     */
    public function addAction($route, $type = 'main') {
        $routeArgs = $this->buildRoutes($route);
        if ($routeArgs === false) {
            $this->breadcrumbs();
            $this->addAction('error/not_found');
            return;
        }
        $action['file'] = $this->controller;
        $action['method'] = $this->method;
        $action['class'] = $this->class;
        $action['context'] = $this->context;
        $action['template'] = $this->template;
        $action['model'] = $this->model;
        $action['args'] = $routeArgs;
        if ($type == 'pre') {
            $this->registry->setElement('pre_page_actions', $action);
            // Pre page actions will be executed immediately
            $this->execute($action);
        } else if ($type == 'post') {
            // Post page actions will be dispatched after main action
            $this->registry->setElement('post_page_actions', $action);
        } else if ($type == 'child') {
            $this->registry->setElement('page_actions', $action, basename($route));
            // Children are executed immediately
            $this->execute($action);
        } else {
           // Set up the breadcrumb trail for the main action
           $this->breadcrumbs();
           $this->registry->setElement('page_actions', $action, 'main_action');
        }
    }

    /**
     * Adds a module as a child action of the main action
     * 
     * This method uses strict path definitions to find and build an action
     * based upon the provided route.  
     * 
     * Modules can include other modules and other children controllers just
     * like any other controller can.
     * 
     * NOTE: The route provided for a module is different than a route for other
     * actions.  The route for a module does not include a context and has the 
     * module name followed by a method and arguments.
     * 
     * @uses Registry
     * @uses Document
     * @param type $route Route in the form /module_name/method/arg/arg/...
     */
    public function addModule($route) {
        $parts = explode('/', str_replace('../', '', (string) $route));
        $moduleName = '';
        $moduleMethod = '';
        $moduleArgs = array();
        foreach ($parts as $key => $value) {
            if ($key == 0) $moduleName = $value;
            if ($key == 1) $moduleMethod = $value;
            if ($key > 1) $moduleArgs[] = $value;
        }
        $moduleDir = SYSTEM_DIR . 'modules/' . $moduleName . '/';
        $moduleWebDir = ROOT_DIR . 'modules/'. $moduleName . '/';
        $moduleUrl = ROOT_URL . 'modules/' . $moduleName . '/';
        $files['css'] = glob($moduleWebDir.'css/*');
        $files['js'] = glob($moduleWebDir.'javascript/*');
        $files['models'] = glob($moduleDir.'model/*');
        //Now add CSS and JS to the document head
        $document = $this->registry->get('document');
        foreach ($files['css'] as $name) {
            $document->addStyle($moduleUrl.'css/'.basename($name));
        }
        foreach ($files['js'] as $name) {
            $document->addScript($moduleUrl.'javascript/'.basename($name));
        }
        $module['file'] = $moduleDir.$moduleName.'.php';
        $module['method'] = ($moduleMethod)? $moduleMethod : 'index';
        $module['class'] = basename(SYSTEM_DIR). '\\modules\\'.$moduleName.'\\'.ucfirst($moduleName);
        $module['template'] = $moduleDir.'template/'.$moduleName.TEMPLATE_EXTENSION;
        $module['model'] = (!empty($files['models'])) ? $files['models'][0] : null;
        $module['args'] = $moduleArgs;
        $this->registry->setElement('page_actions', $module, basename(strtolower($moduleName)));
        // Modules are not dispatched, but executed immediately
        $this->execute($module);
    }
    /**
     * Clear all action arrays
     * 
     * @uses Registry
     */
    public function clearAll() {
        $this->registry->set('page_actions', null);
        $this->registry->set('pre_page_actions', array());
        $this->registry->set('post_page_actions', array());
    }

}

?>